import Web3 from "web3";
import { BigNumber } from "bignumber.js";

const client = {};

let web3 = null;
let logger = null;

const ERC20_ABI = [
  {
    constant: true,
    inputs: [{ "name": "account", "type": "address" }],
    name: "balanceOf",
    outputs: [{ "name": "", "type": "uint256" }],
    type: "function"
  },
  {
    constant: true,
    inputs: [],
    name: "decimals",
    outputs: [{ name: "", type: "uint8" }],
    type: "function",
  },
  {
    constant: true,
    inputs: [],
    name: "symbol",
    outputs: [{ name: "", type: "string" }],
    type: "function",
  },
  {
    constant: true,
    inputs: [],
    name: "name",
    outputs: [{ name: "", type: "string" }],
    type: "function",
  },
  {
    constant: false,
    inputs: [
      { name: "spender", type: "address" },
      { name: "value", type: "uint256" },
    ],
    name: "approve",
    outputs: [{ name: "", type: "bool" }],
    type: "function",
  },
  {
      "constant": true,
      "inputs": [
          {
              "name": "_owner",
              "type": "address"
          },
          {
              "name": "_spender",
              "type": "address"
          }
      ],
      "name": "allowance",
      "outputs": [
          {
              "name": "",
              "type": "uint256"
          }
      ],
      "payable": false,
      "stateMutability": "view",
      "type": "function"
  }
];

const WBNB = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c";

const USDT = "0x55d398326f99059fF775485246999027B3197955";

// PancakeSwap Router 合约地址
const PANCAKE_ROUTER = "0x10ED43C718714eb63d5aA57B78B54704E256024E"; // 主网V2

const ROUTER_ABI = [
  {
    constant: true,
    inputs: [
      { name: "amountIn", type: "uint256" },
      { name: "path", type: "address[]" },
    ],
    name: "getAmountsOut",
    outputs: [{ name: "amounts", type: "uint256[]" }],
    payable: false,
    stateMutability: "view",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountOutMin", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "swapExactETHForTokens",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "payable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountIn", type: "uint256" },
      { internalType: "uint256", name: "amountOutMin", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "swapExactTokensForETH",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      { internalType: "uint256", name: "amountIn", type: "uint256" },
      { internalType: "uint256", name: "amountOutMin", type: "uint256" },
      { internalType: "address[]", name: "path", type: "address[]" },
      { internalType: "address", name: "to", type: "address" },
      { internalType: "uint256", name: "deadline", type: "uint256" },
    ],
    name: "swapExactTokensForTokens",
    outputs: [
      { internalType: "uint256[]", name: "amounts", type: "uint256[]" },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "amountIn",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amountOutMin",
        "type": "uint256"
      },
      {
        "internalType": "address[]",
        "name": "path",
        "type": "address[]"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      }
    ],
    "name": "swapExactTokensForETHSupportingFeeOnTransferTokens",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
];

client.init = (rpcUrl, log) => {
  logger = log;
  web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl));
  web3.utils.toBN = (value) => {
    return new BigNumber(value);
  };
  logger.info("客户端", "初始化成功");
  return web3;
};

client.getContract = (abi, contractAddress, wallet) => {
  assert(web3, "Web3未初始化");
  assert(wallet, "缺少钱包实例");
  const contract = new web3.eth.Contract(abi, contractAddress);
  contract.options.from = wallet.address;
  contract.options.handleRevert = true;
  return contract;
};

client.getAccount = (privateKey) => {
  assert(web3, "Web3未初始化");
  if(privateKey.length!=66){
    logger.error("私钥错误", privateKey);
    throw new Error("私钥错误:"+privateKey);
  }
  return web3.eth.accounts.privateKeyToAccount(privateKey);
};

client.createAccount = async () => {
  if (!web3) {
    throw new Error("Web3未初始化");
  }
  const account = await web3.eth.accounts.create();

  logger.info("钱包创建", `地址: ${account.address}`);
  return account;
};

// BSC Multicall 合约地址
// https://github.com/makerdao/multicall
// https://bscscan.com/address/0x41263cba59eb80dc200f3e2544eda4ed6a90e76c#code
// const MULTICALL_V1_ADDRESS = "0x759c4890cff37fba701c1bee56d22234b890ddd2"; //GGG
// const MULTICALL3_ADDRESS = "0x41263cba59eb80dc200f3e2544eda4ed6a90e76c"; 

const MULTICALL3_ADDRESS = "0xca11bde05977b3631167028862be2a173976ca11";
// Multicall3 ABI（请确认部署链上合约与此 ABI 完全对应）
const MULTICALL3_ABI = [{
  inputs: [{
      components: [{
          name: "target",
          type: "address"
      }, {
          name: "allowFailure",
          type: "bool"
      }, {
          name: "callData",
          type: "bytes"
      }],
      name: "calls",
      type: "tuple[]"
  }],
  name: "aggregate3",
  outputs: [{
      components: [{
          name: "success",
          type: "bool"
      }, {
          name: "returnData",
          type: "bytes"
      }],
      name: "returnData",
      type: "tuple[]"
  }],
  stateMutability: "view",
  type: "function"
}];

// ERC20 合约ABI（须包含 balanceOf 方法）
const ERC20_ABI_01 = [
  // 只展示 balanceOf 方法的 ABI 片段
  {
    "constant": true,
    "inputs": [{ "name": "account", "type": "address" }],
    "name": "balanceOf",
    "outputs": [{ "name": "", "type": "uint256" }],
    "type": "function"
  }
];

// 多地址 + 多代币余额批量查询（Multicall v3 实现）
// walletAddresses: 钱包地址数组
// tokens: 代币数组，每个代币包含 symbol、address 与 decimals
client.getAccountsBalances = async (walletAddresses, tokens) => {
  const multicall = new web3.eth.Contract(MULTICALL3_ABI, MULTICALL3_ADDRESS);
  const calls = [];

  for (const addr of walletAddresses) {
    for (const token of tokens) {
      let callData = "";
      let targetAddress = token.address; // 默认调用 ERC20 token 合约

      if (
        token.address.toLowerCase() === "0x0000000000000000000000000000000000000101"
      ) {
        // 针对原生币 (BNB) 的查询：调用 Multicall 合约自身的 getEthBalance(address)
        const selector = web3.eth.abi.encodeFunctionSignature("getEthBalance(address)");
        // 将钱包地址参数去掉 0x 并左侧补 0 至 64 位
        const param = addr.toLowerCase().replace("0x", "").padStart(64, "0");
        callData = selector + param;
        // 修改调用目标为 Multicall 合约地址，而非特殊地址
        targetAddress = MULTICALL3_ADDRESS;
      } else {
        // 普通 ERC20 代币调用 balanceOf(address)
        const contract = new web3.eth.Contract(ERC20_ABI_01, token.address);
        callData = contract.methods.balanceOf(addr).encodeABI();
      }

      calls.push({
        target: targetAddress,
        allowFailure: true,
        callData,
      });
    }
  }

  // 批量发起调用
  const returnData = await multicall.methods.aggregate3(calls).call();
  console.log("returnData: ", returnData);
  const results = [];
  let i = 0;
  
  // 解析结果：注意调用顺序与 walletAddresses 与 tokens 的循环嵌套顺序保持一致
  for (const addr of walletAddresses) {
    for (const token of tokens) {
      const { success, returnData: raw } = returnData[i];
      if (success) {
        // 解码 uint256 结果
        const decoded = web3.eth.abi.decodeParameter("uint256", raw);
        // 根据 decimals 转换为浮点值
        const balance = Number(decoded) / 10 ** token.decimals;
        results.push({
          address: addr,
          symbol: token.symbol,
          balance,
        });
      } else {
        results.push({
          address: addr,
          symbol: token.symbol,
          balance: null,
        });
      }
      i++;
    }
  }

  console.table(results);
  return results;
}

// 调用示例（请将地址替换为实际地址）
// const walletAddresses = ['0xYourAddress1Here', '0xYourAddress2Here'];
// const tokenList = [
//   { symbol: 'BNB', address: '0x0000000000000000000000000000000000000101', decimals: 18 },
//   { symbol: 'BUSD', address: '0xe9e7cea3dedca5984780bafc599bd69add087d56', decimals: 18 },
//   { symbol: 'USDT', address: '0x55d398326f99059ff775485246999027b3197955', decimals: 18 }
// ];
// client.getAccountsBalances(walletAddresses, tokenList)
//   .then(results => console.table(results))
//   .catch(console.error);


client.getAccountTokens = async (privateKey, tokenAddress) => {
  assert(web3, "Web3未初始化");
  try {
    let tokens = [];
    const bnb = await client.getAccountToken(privateKey);
    tokens.push(bnb);
    // const usdt=await getToken(privateKey, USDT)
    // tokens.push(usdt);
    if (tokenAddress) {
      const token = await client.getAccountToken(privateKey, tokenAddress);
      tokens.push(token);
    }
    return tokens;
  } catch (error) {
    console.log(error);
    logger.error("余额查询", error.message);
    throw new Error(`查询失败: ${error.message}`);
  }
};

client.getAccountToken = async (privateKey, tokenAddress) => {
  assert(web3, "Web3未初始化");
  try {
    // 通过私钥获取账户地址
    const account = web3.eth.accounts.privateKeyToAccount(privateKey);
    const address = account.address;

    let name;
    let symbol;
    let balance;
    let tokenAddr;
    if (tokenAddress) {
      // 查询ERC20代币余额
      const begin = new Date().getTime();
      const tokenContract = new web3.eth.Contract(ERC20_ABI, tokenAddress);
      const [balanceWei, decimals, tokenName, tokenSymbol] = await Promise.all([
        tokenContract.methods.balanceOf(address).call(),
        tokenContract.methods.decimals().call(),
        tokenContract.methods.name().call(),
        tokenContract.methods.symbol().call(),
      ]);
      balance = BigNumber(balanceWei.toString())
        .dividedBy(10 ** decimals)
        .toFixed(4, 1); //ROUND_DOWN
      name = tokenName;
      symbol = tokenSymbol;
      tokenAddr = tokenAddress;
      const end = new Date().getTime();
      const time = end - begin;
      logger.trace(
        `钱包`,
        `${address}\n${symbol}：${balance}，耗时：${time}ms`
      );
    } else {
      // 查询原生代币余额（BNB）
      const begin = new Date().getTime();
      const balanceWei = await web3.eth.getBalance(address);
      balance = BigNumber(web3.utils.fromWei(balanceWei, "ether")).toFixed(
        6,
        1
      );
      name = "BNB";
      symbol = "BNB";
      tokenAddr = WBNB;
      const end = new Date().getTime();
      const time = end - begin;
      logger.trace(
        `钱包`,
        `${address}\n${symbol}：${balance}，耗时：${time}ms`
      );
    }
    balance = BigNumber(balance).toNumber();
    console.log("typeof balance: ", typeof balance);
    logger.trace("余额查询", `地址: ${address}\n${symbol} 余额: ${balance}`);
    return { balance: balance, address: tokenAddr, name: name, symbol: symbol };
  } catch (error) {
    console.log(error);
    logger.error("余额查询", error.message);
    throw new Error(`查询失败: ${error.message}`);
  }
};

client.batchCollectBNB = async (
  privateKeys,
  toAddress,
  gasLimit = 21000 // 原生币转账一般使用21000 gas
) => {
  // 校验前置条件
  assert(web3, "Web3 未初始化");
  assert(privateKeys?.length > 0, "至少需要提供一个私钥");
  assert(toAddress && web3.utils.isAddress(toAddress), "无效的目标地址");

  try {
    // 根据私钥创建账户对象
    const accounts = privateKeys.map((pk) =>
      web3.eth.accounts.privateKeyToAccount(pk)
    );

    // 统一获取 gasPrice
    const gasPrice = await web3.eth.getGasPrice();

    // 构建每个账户的转账交易数据
    const transactions = [];
    for (const account of accounts) {
      // 获取账户的 BNB 余额（单位为 Wei）
      const balanceWei = await web3.eth.getBalance(account.address);
      
      // 计算该笔交易需要支付的手续费
      const fee = BigInt(gasLimit) * BigInt(gasPrice);
      
      // 如果余额不足以支付手续费，则跳过
      if (BigInt(balanceWei) <= fee) continue;
      
      // 归集金额 = 余额 - 手续费（确保余额足够扣除手续费）
      const amountToSend = (BigInt(balanceWei) - fee).toString();
      
      // 获取账户当前 nonce，避免并发问题（使用 "pending" 状态）
      const nonce = await web3.eth.getTransactionCount(account.address, "pending");

      // 构造转账交易数据
      const tx = {
        from: account.address,
        to: toAddress,
        value: amountToSend,
        gas: gasLimit,
        gasPrice,
        nonce,
      };

      transactions.push({
        account, // 保存账户对象，便于后续签名
        tx,
        balance: balanceWei, // 记录原始余额
        amountToSend, // 实际归集金额
      });
    }

    // 签名并依次发送每笔交易
    const results = [];
    for (const item of transactions) {
      try {
        // 使用账户私钥对交易进行签名
        const signedTx = await item.account.signTransaction(item.tx);
        // 发送签名后的交易
        const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
        logger.info(
          "BNB归集",
          `成功归集 \n钱包:${item.account.address}\n${web3.utils.fromWei(item.amountToSend, "ether")} BNB`
        );
        results.push({
          status: "success",
          txHash: receipt.transactionHash,
          from: item.account.address,
          // 转账金额转换为 BNB 单位
          amount: web3.utils.fromWei(item.amountToSend, "ether"),
        });
      } catch (e) {
        results.push({
          status: "failed",
          from: item.account.address,
          error: e.message,
        });
      }
    }

    // 输出归集日志（处理成功的笔数）
    logger.info(
      "BNB归集",
      `成功处理 ${results.filter((r) => r.status === "success").length}/${transactions.length} 笔交易`
    );
    return results;
  } catch (error) {
    logger.error("BNB归集失败", error.message);
    throw error;
  }
};



client.batchCollectToken = async (
  privateKeys,
  tokenAddress,
  toAddress,
  gasLimit = 60000
) => {
  // 校验前置条件
  assert(web3, "Web3 未初始化");
  assert(privateKeys?.length > 0, "至少需要提供一个私钥");
  assert(tokenAddress && web3.utils.isAddress(tokenAddress), "无效的代币地址");
  assert(toAddress && web3.utils.isAddress(toAddress), "无效的目标地址");

  try {
    // 初始化 ERC20 合约和 MULTICALL3 合约
    const erc20Contract = new web3.eth.Contract(ERC20_ABI, tokenAddress);
    const multicallContract = new web3.eth.Contract(MULTICALL3_ABI, MULTICALL3_ADDRESS);
    // 根据私钥创建账户对象
    const accounts = privateKeys.map((pk) =>
      web3.eth.accounts.privateKeyToAccount(pk)
    );

    // 构建每个账户的 balanceOf 调用数据，利用 MULTICALL3 批量查询余额
    const balanceCalls = accounts.map((account) => ({
      target: tokenAddress,
      callData: erc20Contract.methods.balanceOf(account.address).encodeABI(),
    }));

    // 调用 MULTICALL3 合约的 tryAggregate 方法，一次性获取所有账户的余额
    const { returnData: balanceResults } = await multicallContract.methods
      .tryAggregate(false, balanceCalls)
      .call();

    // 统一获取 gasPrice
    const gasPrice = await web3.eth.getGasPrice();

    // 逐条构造需要转账的交易数据
    const transactions = [];
    for (let i = 0; i < balanceResults.length; i++) {
      const { success, returnData } = balanceResults[i];
      if (!success) continue; // 调用失败直接跳过

      // 解码 balanceOf 的返回数据
      const balanceWei = web3.eth.abi.decodeParameter("uint256", returnData);
      // 如果余额为 0 则不构造转账
      if (new BigNumber(balanceWei).isZero()) continue;

      const account = accounts[i];
      // 获取账户当前的 nonce（使用 "pending" 状态避免并发问题）
      const nonce = await web3.eth.getTransactionCount(account.address, "pending");
      // 构造 ERC20 的 transfer 调用数据
      const txData = erc20Contract.methods.transfer(toAddress, balanceWei).encodeABI();

      transactions.push({
        account, // 保存账户对象，便于后续签名
        tx: {
          from: account.address,
          to: tokenAddress,
          data: txData,
          gas: gasLimit,
          gasPrice,
          nonce,
        },
        balance: balanceWei, // 便于记录转移金额
      });
    }

    // 依次处理每笔转账交易：签名并发送
    const results = [];
    for (const item of transactions) {
      try {
        // 使用账户私钥对交易进行签名
        const signedTx = await item.account.signTransaction(item.tx);
        // 发送签名后的交易
        const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);

        results.push({
          status: "success",
          txHash: receipt.transactionHash,
          from: item.tx.from,
          amount: web3.utils.fromWei(item.balance, "ether"),
        });
      } catch (e) {
        results.push({
          status: "failed",
          from: item.tx.from,
          error: e.message,
        });
      }
    }

    // 输出批量归集日志（处理成功的数量）
    logger.info(
      "批量归集",
      `成功处理 ${results.filter((r) => r.status === "success").length}/${
        transactions.length
      } 笔交易`
    );
    return results;
  } catch (error) {
    logger.error("归集失败", error.message);
    throw error;
  }
};

client.getSymbol = async (tokenAddress) => {
  if (!tokenAddress) {
    return "无";
  }
  try {
    const tokenContract = new web3.eth.Contract(ERC20_ABI, tokenAddress);
    return await tokenContract.methods.symbol().call();
  } catch (err) {
    return "无";
  }
};

client.appendToMetamask = async (privateKey) => {
  assert(web3, "Web3未初始化");

  if (!window.ethereum?.isMetaMask) {
    throw new Error("MetaMask扩展未检测到，请先安装");
  }

  try {
    // 清理并验证私钥格式
    const cleanPrivateKey = privateKey.replace(/^0x/, "");
    if (!/^[a-fA-F0-9]{64}$/.test(cleanPrivateKey)) {
      throw new Error("无效的私钥格式（需要64位十六进制字符）");
    }

    // 创建账户对象
    const account = web3.eth.accounts.privateKeyToAccount(
      `0x${cleanPrivateKey}`
    );

    // 添加BSC主网配置（如果尚未添加）
    await window.ethereum.request({
      method: "wallet_addEthereumChain",
      params: [
        {
          chainId: "0x38", // BSC主网chainId 56
          chainName: "Binance Smart Chain Mainnet",
          nativeCurrency: {
            name: "BNB",
            symbol: "BNB",
            decimals: 18,
          },
          rpcUrls: ["https://bsc-dataseed.binance.org/"],
          blockExplorerUrls: ["https://bscscan.com/"],
        },
      ],
    });

    // 请求账户连接
    await window.ethereum.request({
      method: "eth_requestAccounts",
      params: [{ eth_accounts: {} }],
    });

    // 通过添加代币的方式添加账户（当前MetaMask的限制）
    const result = await window.ethereum.request({
      method: "wallet_watchAsset",
      params: {
        type: "ERC20",
        options: {
          address: account.address,
          symbol: "ACT", // 自定义代币符号
          decimals: 18,
        },
      },
    });

    if (!result) {
      throw new Error("用户取消了操作");
    }

    logger.success("账户已成功添加", `地址: ${account.address}`);
    return {
      address: account.address,
      privateKey: `0x${cleanPrivateKey}`,
    };
  } catch (error) {
    logger.error("添加账户失败", error.message);
    throw new Error(`操作失败: ${error.message}`);
  }
};

client.transfer = async (account, to, amount) => {
  assert(web3, "Web3未初始化");
  logger.info("开始转账", `${amount} BNB ${to}`);
  try {
    // 参数校验
    if (!account || !to || !amount) {
      throw new Error("缺少必要参数");
    }
    if (!/^0x[a-fA-F0-9]{40}$/.test(account.address)) {
      throw new Error("无效的发送地址");
    }
    if (!/^0x[a-fA-F0-9]{40}$/.test(to)) {
      throw new Error("无效的接收地址");
    }

    const amountInWei = web3.utils.toWei(amount.toString(), "ether");

    // 获取账户余额并校验是否小于0.01 BNB
    const balanceWei = await web3.eth.getBalance(account.address);
    const minBalanceWei = web3.utils.toWei("0.01", "ether");

    if (BigInt(balanceWei) < BigInt(minBalanceWei)) {
      throw new Error("账户余额不足，需大于 0.01 BNB");
    }

    const txData = {
      from: account.address,
      to: to,
      value: amountInWei,
      gas: 21000,
      gasPrice: await web3.eth.getGasPrice(),
    };

    const receipt = await executeTrade(txData, account);
    logger.success(
      "转账成功",
      `${amount} BNB ${to} \n交易哈希: ${receipt.transactionHash}`
    );
    return receipt;
  } catch (error) {
    logger.error("转账失败", `${amount} BNB ${to} \n ${error.message}`);
    throw error;
  }
};


const executeTrade = async (txConfig, wallet) => {
  console.log(
    `executeTrade txConfig: ${JSON.stringify(
      txConfig
    )} \n wallet: ${JSON.stringify(wallet)}`
  );
  // Convert wallet to plain object to avoid circular references
  const plainWallet = {
    address: wallet.address,
    privateKey: wallet.privateKey,
    signTransaction: wallet.signTransaction,
  };

  if (!plainWallet?.signTransaction) {
    throw new Error("Invalid wallet instance - missing signTransaction method");
  }

  try {
    const signedTx = await plainWallet.signTransaction(txConfig);
    return await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
  } catch (error) {
    console.log(error);
    logger.error("签名失败", JSON.stringify(error));
    throw error;
  }
};

client.buy = async (
  walletPrivateKey,
  tokenAddress,
  poolType,
  poolAmount,
  slippage = 100
) => {
  assert(web3, "Web3未初始化");
  let fromAddress = addr(poolType)
  try {
    const senderAccount =
      web3.eth.accounts.privateKeyToAccount(walletPrivateKey);
    const router = new web3.eth.Contract(ROUTER_ABI, PANCAKE_ROUTER);

    // 处理不同基础代币的兑换逻辑
    if (poolType === "bnb") {
      // 直接使用BNB购买
      return await swapExactETHForTokens(
        walletPrivateKey,
        fromAddress,
        tokenAddress,
        poolAmount,
        slippage
      );
    } else {
      // 处理ERC20代币兑换（USDT等）
      const fromContract = new web3.eth.Contract(ERC20_ABI, fromAddress);
      const decimals = await fromContract.methods.decimals().call();
      const amountInWei = new BigNumber(poolAmount)
        .multipliedBy(10 ** decimals)
        .toFixed(0);

      // 授权路由器使用代币
      const approveTx = {
        from: senderAccount.address,
        to: fromAddress,
        data: fromContract.methods
          .approve(PANCAKE_ROUTER, amountInWei)
          .encodeABI(),
        gas: 100000,
      };

      const signedApproveTx = await web3.eth.accounts.signTransaction(
        approveTx,
        walletPrivateKey
      );
      await web3.eth.sendSignedTransaction(signedApproveTx.rawTransaction);

      // 执行代币兑换
      return await swapExactTokensForTokens(
        walletPrivateKey,
        fromAddress,
        tokenAddress,
        amountInWei,
        slippage
      );
    }
  } catch (error) {
    logger.error("购买失败", error.message);
    throw error;
  }
};



const swapExactETHForTokens = async (
  walletPrivateKey,
  fromAddress,
  toAddress,
  amount,
  slippage
) => {
  const senderAccount = web3.eth.accounts.privateKeyToAccount(walletPrivateKey);
  const router = new web3.eth.Contract(ROUTER_ABI, PANCAKE_ROUTER);

  // 获取兑换报价
  const amounts = await router.methods
    .getAmountsOut(web3.utils.toWei(amount.toString(), "ether"), [
      fromAddress,
      toAddress,
    ])
    .call();

  if (!amounts || amounts.length < 2) {
    throw new Error("获取兑换数量失败");
  }

  // 计算滑点
  const slippageBasisPoints = Math.min(
    slippage + Math.floor(Math.random() * 50),
    2000
  );
  const amountOutMin = new BigNumber(amounts[1].toString())
    .multipliedBy(10000 - slippageBasisPoints)
    .dividedBy(10000)
    .toFixed(0);

  // 构建交易参数
  const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
  const path = [fromAddress, toAddress];

  // 估算Gas
  const gasEstimate = await router.methods
    .swapExactETHForTokens(amountOutMin, path, senderAccount.address, deadline)
    .estimateGas({
      from: senderAccount.address,
      value: web3.utils.toWei(amount, "ether"),
    });

  // 获取Gas价格
  const gasPrice = await web3.eth.getGasPrice();

  // 构建交易
  const tx = {
    from: senderAccount.address,
    to: PANCAKE_ROUTER,
    value: web3.utils.toWei(amount, "ether"),
    gas: gasEstimate,
    gasPrice: gasPrice,
    data: router.methods
      .swapExactETHForTokens(
        amountOutMin,
        path,
        senderAccount.address,
        deadline
      )
      .encodeABI(),
  };

  // 签名并发送交易
  const signedTx = await web3.eth.accounts.signTransaction(
    tx,
    walletPrivateKey
  );
  const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
  logger.success("购买成功", `交易哈希:\n${receipt.transactionHash}`);
  return receipt;
};

const swapExactTokensForTokens = async (
  walletPrivateKey,
  fromAddress,
  toAddress,
  amountInWei,
  slippage
) => {
  const senderAccount = web3.eth.accounts.privateKeyToAccount(walletPrivateKey);
  const router = new web3.eth.Contract(ROUTER_ABI, PANCAKE_ROUTER);

  // 获取兑换报价
  const amounts = await router.methods
    .getAmountsOut(amountInWei, [fromAddress, toAddress])
    .call();

  if (!amounts || amounts.length < 2) {
    throw new Error("获取兑换数量失败");
  }

  // 计算滑点
  const slippageBasisPoints = Math.min(
    slippage + Math.floor(Math.random() * 50),
    2000
  );
  const amountOutMin = new BigNumber(amounts[1].toString())
    .multipliedBy(10000 - slippageBasisPoints)
    .dividedBy(10000)
    .toFixed(0);

  // 构建交易参数
  const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
  const path = [fromAddress, toAddress];

  // 估算Gas
  const gasEstimate = await router.methods
    .swapExactTokensForTokens(
      amountInWei,
      amountOutMin,
      path,
      senderAccount.address,
      deadline
    )
    .estimateGas({ from: senderAccount.address });

  // 获取Gas价格
  const gasPrice = await web3.eth.getGasPrice();

  // 构建交易
  const tx = {
    from: senderAccount.address,
    to: PANCAKE_ROUTER,
    gas: gasEstimate,
    gasPrice: gasPrice,
    data: router.methods
      .swapExactTokensForTokens(
        amountInWei,
        amountOutMin,
        path,
        senderAccount.address,
        deadline
      )
      .encodeABI(),
  };

  // 签名并发送交易
  const signedTx = await web3.eth.accounts.signTransaction(
    tx,
    walletPrivateKey
  );
  const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
  logger.success("购买成功", `交易哈希:\n${receipt.transactionHash}`);
  return receipt;
};

/**
 * 卖出私钥对应钱包中的全部某种代币，兑换成BNB
 * @param {string} privateKey 私钥
 * @param {string} tokenAddress 要出售的代币合约地址
 */
client.sell = async (privateKey, tokenAddress, poolType, slippage = 100) => {
  assert(web3, "Web3未初始化");
  let targetAddress = addr(poolType)
  try {
    // 初始化账户
    const account = web3.eth.accounts.privateKeyToAccount(privateKey);
    const walletAddress = account.address;
    web3.eth.accounts.wallet.add(account);

    const token = new web3.eth.Contract(ERC20_ABI, tokenAddress);
    const router = new web3.eth.Contract(ROUTER_ABI, PANCAKE_ROUTER);

    // 查询代币余额
    const balance = await token.methods.balanceOf(walletAddress).call();
    if (balance === '0') {
      logger.info('卖出', `[${walletAddress}] 余额为 0，无需出售`);
      throw new Error('钱包中没有代币');
    }

    const slippagePercent = Math.min(Math.max(slippage, 1), 100);
    const slippageDecimal = slippagePercent / 100;

    // 获取兑换路径和预估 BNB 数额
    const path = [tokenAddress, targetAddress];
    const amountsOut = await router.methods.getAmountsOut(balance, path).call();
    const estimatedBNB = new BigNumber(amountsOut[1]);
    const minBNBOut = estimatedBNB.multipliedBy(1 - slippageDecimal).toFixed(0);

    logger.info('卖出', `预估可得: ${web3.utils.fromWei(estimatedBNB.toFixed(0))} BNB`);
    logger.info('卖出', `最小接受: ${web3.utils.fromWei(minBNBOut)} BNB（滑点 ${slippagePercent}%）`);

    // 如果当前授权额度不足，则先进行授权交易
    const allowance = await token.methods.allowance(walletAddress, PANCAKE_ROUTER).call();
    if (new BigNumber(allowance).lt(balance)) {
      logger.info('卖出', '授权中...');
      const approveTx = token.methods.approve(PANCAKE_ROUTER, balance);
      const gas = await approveTx.estimateGas({ from: walletAddress });
      const approveData = approveTx.encodeABI();

      // 获取当前账号最新的 nonce（使用 pending 模式保证包含未确认交易）
      const approveNonce = await web3.eth.getTransactionCount(walletAddress, 'pending');
      const txApprove = {
        from: walletAddress,
        to: tokenAddress,
        data: approveData,
        gas,
        nonce: approveNonce
      };

      const signedApprove = await web3.eth.accounts.signTransaction(txApprove, privateKey);
      await web3.eth.sendSignedTransaction(signedApprove.rawTransaction);
      logger.info('卖出', '授权完成');
    }

    // 进行代币兑换交易
    const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
    const swapTx = router.methods.swapExactTokensForETHSupportingFeeOnTransferTokens(
      balance,
      minBNBOut,
      path,
      walletAddress,
      deadline
    );
    const gas = await swapTx.estimateGas({ from: walletAddress });
    const txData = swapTx.encodeABI();

    // 获取最新的 nonce，用于swap交易
    const swapNonce = await web3.eth.getTransactionCount(walletAddress, 'pending');
    const txSwap = {
      from: walletAddress,
      to: PANCAKE_ROUTER,
      data: txData,
      gas,
      nonce: swapNonce
    };

    const signedSwapTx = await web3.eth.accounts.signTransaction(txSwap, privateKey);
    const receipt = await web3.eth.sendSignedTransaction(signedSwapTx.rawTransaction);
    logger.info('卖出', `[${walletAddress}] 出售成功: ${receipt.transactionHash}`);
  } catch (err) {
    logger.info('卖出', `❌ 错误: ${err.message || err}`);
    throw err;
  }
}


client.getToken= async (tokenAddress,poolType) => {
  assert(web3, "Web3未初始化");
  assert(tokenAddress, "合约地址必填");
  assert(poolType, "池子类型必填");
  const tokenContract = new web3.eth.Contract(ERC20_ABI, tokenAddress);
  const [balanceWei, decimals, tokenName, tokenSymbol] = await Promise.all([
        tokenContract.methods.balanceOf(tokenAddress).call(),
        tokenContract.methods.decimals().call(),
        tokenContract.methods.name().call(),
        tokenContract.methods.symbol().call(),
      ]);
  const symbolA=tokenSymbol;
  const symbolB=await symbol(addr(poolType));
  // await logger.trace('查询价格', `查询 ${symbolA} / ${symbolB} 价格...`);
  const amountIn = web3.utils.toWei('1', 'ether'); // 假设查询 1 个代币的价格
  const router = new web3.eth.Contract(ROUTER_ABI, PANCAKE_ROUTER);
  try {
    const amounts=await router.methods.getAmountsOut(amountIn, [tokenAddress, addr(poolType)]).call()
    const priceInWBNB = web3.utils.fromWei(amounts[1], 'ether');
    const BNBPriceInUSDT = await getBNBPriceInUSDT();
    const priceInUSDT = BigNumber(priceInWBNB * BNBPriceInUSDT).toFixed(10);
    // console.log(`1 Token = ${priceInWBNB} WBNB`);
    return {
      name: tokenName, 
      symbol: tokenSymbol,
      decimals:decimals,
      balance: balanceWei, 
      price:priceInUSDT,
      pair: tokenSymbol+"/"+symbolB,
      address: tokenAddress };
  } catch (error) {
    logger.error("查询", `查询价格失败：${error.message}`);
    throw error;
  }
}

async function getBNBPriceInUSDT() {
  const amountIn = web3.utils.toWei('1', 'ether'); // 1 BNB
  const routerContract = new web3.eth.Contract(ROUTER_ABI, PANCAKE_ROUTER);
  const amounts = await routerContract.methods
    .getAmountsOut(amountIn, [WBNB, USDT])
    .call();
  const priceInUSDT = web3.utils.fromWei(amounts[1], 'ether');
  // logger.info("查询",`当前 BNB 价格: $${priceInUSDT}`);
  return priceInUSDT;
}

const addr=(poolType) => {
  if (poolType === "bnb") {
    return WBNB;
  } else if (poolType === "usdt") {
    return USDT;
  } else {
    logger.error("兑换失败", `池子类型：${poolType} \n 不支持的池子类型`);
    throw new Error(`池子类型：${poolType} \n 不支持的池子类型`);
  }
}


const symbol=async(address) => {
  if (address === WBNB) {
    return "WBNB";
  } else if (address === USDT) {
    return "USDT";
  } else {
    logger.error("兑换失败", `池子类型：${poolType} \n 不支持的池子类型`);
    throw new Error(`池子类型：${poolType} \n 不支持的池子类型`);
  }
}

  


const assert = (condition, message) => {
  if (!condition) {
    throw new Error(message || "Assertion failed");
  }
};

/**
 * 增发钱包编码
 * @param {*} address 
 * @returns 
 */
client.encodeSeo= (address) => {
    // 编码地址（注意：encodePacked 会将地址按原样编码为字节数组）
    const addressBytes = Buffer.from(address.slice(2), 'hex');
    const hash = crypto.createHash('sha256').update(addressBytes).digest();
    const encode='0x' + hash.toString('hex');
    console.log(`address: ${address} \n encode: ${encode}`);
    return encode;
}

export default client;
